<template>
	<div class="sys-update-container">
		<div>
			<NoticeBar text="系统更新管理，请慎重操作！" style="margin: 4px" />
		</div>
		<el-container style="height: calc(100vh - 150px);">
			<el-aside v-auth="'sysUpdate:list'" width="220px" class="backup-list">
				<p class="backup-list-description">备份列表</p>
				<el-scrollbar>
					<div class="backup-items">
						<div v-for="(backup, index) in state.backups" :key="index" class="backup-item" @mouseenter="hovered = index" @mouseleave="hovered = null">
							<el-button type="text" :class="{ 'selected-backup': state.selectedBackup === backup, 'hovered-backup': hovered === index }" @click="() => state.selectedBackup = backup">
								{{ backup.fileName }}
							</el-button>
						</div>
					</div>
				</el-scrollbar>
			</el-aside>
			<el-main v-auth="'sysUpdate:logs'" class="log-terminal-container">
				<div class="toolbar">
					<el-button-group>
						<el-button v-auth="'sysUpdate:update'" v-reclick="5000" :disabled="state.isUpdating" @click="handleAction('update')">更新</el-button>
						<el-button v-auth="'sysUpdate:restore'" v-reclick="5000" :disabled="!canRestore || state.isUpdating || !state.selectedBackup" @click="handleAction('restore')">还原</el-button>
						<el-button v-auth="'sysUpdate:clear'" v-reclick="5000" :disabled="state.isUpdating" @click="clearLogs">清空</el-button>
						<el-button v-auth="'sysUpdate:webHookKey'" v-reclick="5000" @click="getWebHookKey">获取密钥</el-button>
					</el-button-group>
				</div>
				<div class="log-terminal">
					<div class="terminal-output" ref="terminalOutput">
						<pre>{{ state.logOutput }}</pre>
					</div>
				</div>
			</el-main>
		</el-container>
	</div>
</template>

<script setup lang="ts">
import { reactive, computed, onMounted, nextTick, ref, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from "element-plus";
import { getAPI } from "/@/utils/axios-utils";
import { authAll } from "/@/utils/authFunction";
import { BackupOutput, SysUpdateApi } from "/@/api-services";
import commonFunction from "/@/utils/commonFunction";
import NoticeBar from "/@/components/noticeBar/index.vue";

const { copyText } = commonFunction();
const state = reactive({
	selectedBackup: null as BackupOutput | null,
	backups: [] as BackupOutput[],
	isUpdating: false,
	logOutput: '',
});

// 计算属性 canRestore
const canRestore = computed(() => !!state.selectedBackup);

// 引用元素
const terminalOutput = ref<HTMLElement | null>(null);

// 新增的悬停索引变量
const hovered = ref<number | null>(null);

let refreshInterval: number;

// 获取初始数据
const fetchData = async () => {
	try {
		state.backups = (await getAPI(SysUpdateApi).apiSysUpdateListPost()).data.result ?? [];
		await refreshLog();
	} catch (error) {
		handleError('获取数据失败', error);
	}
};

// 刷新日志
const refreshLog = async () => {
	try {
		const response = await getAPI(SysUpdateApi).apiSysUpdateLogsGet();
		state.logOutput = (response.data.result ?? []).join('\n');
		scrollToBottom(); // 更新日志后立即滚动到底部
	} catch (error) {
		handleError('获取日志失败', error);
	}
};

// 滚动到底部
const scrollToBottom = () => {
	nextTick(() => {
		if (terminalOutput.value) {
			terminalOutput.value.scrollTop = terminalOutput.value.scrollHeight;
		}
	});
};

// 启动/停止日志刷新定时器
const toggleRefreshTimer = (start: boolean) => {
	if (start && !refreshInterval) {
		refreshInterval = window.setInterval(refreshLog, 300);
	} else if (!start && refreshInterval) {
		window.clearInterval(refreshInterval);
		refreshInterval = 0;
	}
};

// 处理动作
const handleAction = async (action: 'update' | 'restore') => {
	if (state.isUpdating) return;

	state.isUpdating = true;
	toggleRefreshTimer(true);

	try {
		switch (action) {
			case 'update':
				await getAPI(SysUpdateApi).apiSysUpdateUpdatePost({ timeout: -1 });
				ElMessage.success('更新成功');
				fetchData();
				break;
			case 'restore':
				ElMessageBox.confirm(`确定要还原到 ${state.selectedBackup?.fileName} ?`, '提示', {
					confirmButtonText: '确定',
					cancelButtonText: '取消',
					type: 'warning',
				}).then(async () => {
					await getAPI(SysUpdateApi).apiSysUpdateRestorePost({ fileName: state.selectedBackup?.fileName } as any);
					ElMessage.success('还原成功');
				});
				break;
		}
	} catch (error) {
		handleError(`执行${action}失败`, error);
	} finally {
		toggleRefreshTimer(false);
		state.isUpdating = false;
	}
};

// 清空日志
const clearLogs = async () => {
	try {
		state.logOutput = '';
		await getAPI(SysUpdateApi).apiSysUpdateClearGet();
		ElMessage.success('日志已清空');
	} catch (error) {
		handleError('清空日志失败', error);
	}
};

// 获取密钥
const getWebHookKey = async () => {
	try {
		const res = await getAPI(SysUpdateApi).apiSysUpdateWebHookKeyGet();
		if (res.data.result) copyText(res.data.result);
	} catch (error) {
		handleError('获取密钥失败', error);
	}
}

// 错误处理
const handleError = (message: string, error: any) => {
	ElMessage.error(`${message}，请稍后再试。`);
};

onMounted(() => {
	if (!authAll(['sysUpdate:list', 'sysUpdate:logs'])) return;
	fetchData();
	toggleRefreshTimer(true);
});

onUnmounted(() => {
	if (!authAll(['sysUpdate:list', 'sysUpdate:logs'])) return;
	toggleRefreshTimer(false);
});
</script>

<style scoped>

.sys-update-container {
	display: flex;
	height: 100%;
	background-color: #f0f2f5;
}

.backup-list-description {
	margin-bottom: 10px;
	color: #909399;
}

.backup-list {
	background-color: #ffffff;
	padding: 20px;
	box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
	border-radius: 8px;
	transition: box-shadow 0.3s ease-in-out;
	max-height: 100%;
	overflow: hidden;
}

.backup-items {
	max-height: calc(100vh - 40px);
	overflow-y: auto;
	overflow-x: hidden;
}

.backup-item {
	margin-bottom: 10px;
	transition: transform 0.2s;
}

.backup-item:hover {
	transform: translateX(5px);
}

.selected-backup, .hovered-backup {
	font-weight: bold;
	color: #409eff;
}

.action-button {
	margin-top: 8px;
	transition: background-color 0.3s;
}

.action-button:hover {
	background-color: #ecf5ff;
}

.log-terminal-container {
	flex-grow: 1;
	padding: 20px;
	display: flex;
	flex-direction: column;
	background-color: #ffffff;
	border-radius: 8px;
	box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
	transition: box-shadow 0.3s ease-in-out;
}

.toolbar {
	margin-bottom: 5px;
	padding: 5px 10px 5px 10px;
	background-color: #ffffff;
	border-radius: 4px;
	box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
	display: flex;
	flex-direction: row;
	align-items: center;
	justify-content: flex-start;
	gap: 8px;
}

.log-terminal {
	background-color: #2c3e50;
	color: #ecf0f1;
	border-radius: 4px;
	flex-grow: 1;
	position: relative;
	overflow: hidden;
	box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.terminal-output {
	padding: 20px;
	height: 100%;
	overflow-y: auto;
	white-space: pre-wrap;
	word-wrap: break-word;
	font-family: 'Courier New', Courier, monospace;
	font-size: 14px;
	line-height: 1.5;
}
</style>